home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-13
/
xvisrc.zip
/
EX_CMDS1.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-07-28
|
19KB
|
860 lines
/* Copyright (c) 1990,1991,1992 Chris and John Downey */
#ifndef lint
static char *sccsid = "@(#)ex_cmds1.c 2.1 (Chris & John Downey) 7/29/92";
#endif
/***
* program name:
xvi
* function:
PD version of UNIX "vi" editor, with extensions.
* module name:
ex_cmds1.c
* module function:
File, window and buffer-related command functions
for ex (colon) commands.
* history:
STEVIE - ST Editor for VI Enthusiasts, Version 3.10
Originally by Tim Thompson (twitch!tjt)
Extensive modifications by Tony Andrews (onecom!wldrdg!tony)
Heavily modified by Chris & John Downey
***/
#include "xvi.h"
#ifdef MEGAMAX
overlay "ex_cmds1"
#endif
static char **files; /* list of input files */
static int numfiles; /* number of input files */
static int curfile; /* number of the current file */
char *altfilename;
static long altfileline;
static char nowrtmsg[] = "No write since last change (use ! to override)";
static char nowrtbufs[] = "Some buffers not written (use ! to override)";
static bool_t more_files P((void));
void
do_quit(window, force)
Xviwin *window;
bool_t force;
{
Xviwin *wp;
bool_t changed;
bool_t canexit;
if (force) {
canexit = TRUE;
} else {
/*
* See if any buffers remain modified and unwritten.
*/
changed = FALSE;
wp = window;
do {
if (is_modified(wp->w_buffer)) {
changed = TRUE;
}
} while ((wp = next_window(wp)) != window);
if (changed) {
show_error(window, nowrtbufs);
canexit = FALSE;
} else {
canexit = ! more_files();
}
}
if (canexit) {
sys_exit(0);
}
}
/*
* Split the current window into two, leaving both windows mapped
* onto the same buffer.
*/
void
do_split_window(window)
Xviwin *window;
{
Xviwin *newwin;
newwin = split_window(window);
if (newwin == NULL) {
show_error(window, "No more windows!");
return;
}
map_window_onto_buffer(newwin, window->w_buffer);
/*
* Update the status line of the old window
* (since it will have been moved).
* Also update the window - this will almost certainly
* have no effect on the screen, but is necessary.
*/
show_file_info(window);
update_window(window);
/*
* Show the new window.
*/
init_sline(newwin);
update_window(newwin);
show_file_info(newwin);
/*
* Update the global window variable.
*/
curwin = newwin;
}
/*
* Open a new buffer window, with a possible filename arg.
*
* do_buffer() is responsible for updating the screen image for the
* old window, but not the new one, since we may want to move to a
* different location in the new buffer (e.g. for a tag search).
*/
bool_t
do_buffer(window, filename)
Xviwin *window;
char *filename;
{
Buffer *buffer;
Buffer *new;
Xviwin *newwin;
buffer = window->w_buffer;
if (window->w_nrows < (MINROWS + 1) * 2) {
show_error(window, "Not enough room!");
return(FALSE);
}
new = new_buffer();
if (new == NULL) {
show_error(window, "No more buffers!");
return(FALSE);
}
newwin = split_window(window);
if (newwin == NULL) {
free_buffer(new);
show_error(window, "No more windows!");
return(FALSE);
}
map_window_onto_buffer(newwin, new);
/*
* Update the status lines of each buffer.
*
* Even if (echo & e_SHOWINFO) is turned off, show_file_info()
* will always call update_sline(), which is what we really
* need here.
*
* Note that we don't need to call move_window_to_cursor() for
* the old window until it becomes the current window again.
*/
show_file_info(window);
init_sline(newwin);
if (filename != NULL) {
(void) do_edit(newwin, FALSE, filename);
} else {
new->b_filename = new->b_tempfname = NULL;
show_file_info(newwin);
}
update_window(window);
/*
* The current buffer (a global variable) has
* to be updated here. No way around this.
*/
curbuf = new;
curwin = newwin;
return(TRUE);
}
/*
* "close" (the current window).
*/
void
do_close_window(win, force)
Xviwin *win;
bool_t force;
{
Buffer *buffer;
Xviwin *best;
buffer = win->w_buffer;
if (is_modified(buffer) && !force && buffer->b_nwindows < 2) {
/*
* Don't close a modified buffer.
*/
show_error(win, nowrtmsg);
} else if (next_window(win) != win || !more_files()) {
Xviwin *w;
/*
* We can close this window if:
*
* (
* the buffer has not been modified
* or they are forcing the close
* or there are other windows onto this buffer
* )
* AND
* (
* there are other windows still open
* or there are no more files to be edited
* )
*/
/*
* Find an adjacent window to take up the screen
* space used by the one being closed.
*/
best = NULL;
for (w = next_window(win); w != win; w = next_window(w)) {
if (w->w_cmdline + 1 == win->w_winpos ||
w->w_winpos - 1 == win->w_cmdline ||
w->w_nrows == 0) {
/*
* We have found an adjacent window;
* if it is the first such, or if
* it is smaller than the previous
* best, it is now the new best.
*/
if (best == NULL || w->w_nrows < best->w_nrows) {
best = w;
}
}
}
if (best == NULL) {
sys_exit(0);
}
if (buffer->b_nwindows == 1 && buffer->b_filename != NULL) {
/*
* Before we free the buffer, save its filename.
*/
if (altfilename != NULL) {
free(altfilename);
}
altfilename = buffer->b_filename;
buffer->b_filename = NULL;
altfileline = lineno(buffer,
win->w_cursor->p_line);
}
/*
* Now "best" points to the smallest adjacent window;
* amalgamate the spaces used.
*/
if (best->w_winpos > win->w_winpos) {
best->w_winpos = win->w_winpos;
}
best->w_nrows += win->w_nrows;
best->w_cmdline = best->w_winpos + best->w_nrows - 1;
free_window(win);
if (buffer->b_nwindows == 0) {
free_buffer(buffer);
}
/*
* Have to update the globals "curbuf" and "curwin" here.
*/
curwin = best;
curbuf = best->w_buffer;
{
unsigned savecho;
savecho = echo;
/*
* Adjust position of new current window
* within buffer before updating it, to avoid
* wasting screen output - but don't do any
* scrolling at this stage because the old
* window is still on the screen.
*/
echo &= ~(e_CHARUPDATE | e_SHOWINFO | e_SCROLL);
move_window_to_cursor(curwin);
echo = savecho;
}
update_window(curwin);
show_file_info(curwin);
}
}
/*
* Close current window.
*
* If it is the last window onto the buffer, also close the buffer.
*
* If the buffer has been modified, we must write it out before closing it.
*/
void
do_xit(window)
Xviwin *window;
{
Buffer *buffer;
buffer = window->w_buffer;
if (is_modified(buffer) && buffer->b_nwindows < 2) {
if (buffer->b_filename != NULL) {
if (!writeit(window, buffer->b_filename,
(Line *) NULL, (Line *) NULL, FALSE)) {
return;
}
} else {
show_error(window, "No output file");
return;
}
}
do_close_window(window, FALSE);
}
/*
* Edit the given filename in the given buffer,
* replacing any current contents. Note that the
* screen is not updated, since there are routines
* which use this function before moving the cursor
* to a different position in the file.
*
* Returns TRUE for success, FALSE for failure.
*/
bool_t
do_edit(window, force, arg)
Xviwin *window;
bool_t force;
char *arg;
{
long line = 1; /* line # to go to in new file */
long nlines; /* no of lines read from file */
Line *head; /* start of list of lines */
Line *tail; /* last element of list of lines */
bool_t readonly; /* true if cannot write file */
Buffer *buffer;
Xviwin *wp;
buffer = window->w_buffer;
if (!force && is_modified(buffer)) {
show_error(window, nowrtmsg);
return(FALSE);
}
if (arg == NULL || arg[0] == '\0') {
/*
* No filename specified; we must already have one.
*/
if (buffer->b_filename == NULL) {
show_error(window, "No filename");
return(FALSE);
}
} else /* arg != NULL */ {
/*
* Filename specified.
*/
/*
* First detect a ":e" on the current file. This is mainly
* for ":ta" commands where the destination is within the
* current file.
*/
if (buffer->b_filename != NULL &&
strcmp(arg, buffer->b_filename) == 0) {
if (!is_modified(buffer) || (is_modified(buffer) && !force)) {
return(TRUE);
}
}
/*
* Detect an edit of the alternate file, and set
* the line number.
*/
if (altfilename != NULL && strcmp(arg, altfilename) == 0) {
line = altfileline;
}
/*
* Save the name of the previous file.
* If the strsave() of the new filename
* fails, we will have lost the previous
* value of altfilename. What a shame.
*/
if (buffer->b_filename != NULL) {
if (altfilename != NULL)
free(altfilename);
altfilename = strsave(buffer->b_filename);
altfileline = lineno(buffer, window->w_cursor->p_line);
}
/*
* Edit a named file.
*/
buffer->b_filename = strsave(arg);
if (buffer->b_filename == NULL)
return(FALSE);
if (buffer->b_tempfname != NULL)
free(buffer->b_tempfname);
buffer->b_tempfname = NULL;
}
/*
* Clear out the old buffer and read the file.
*/
if (clear_buffer(buffer) == FALSE) {
show_error(window, "Out of memory");
return(FALSE);
}
/*
* Be sure to re-map all window structures onto the buffer,
* in order to eliminate any pointers into the old buffer.
*/
wp = window;
do {
if (wp->w_buffer != buffer)
continue;
unmap_window(wp);
map_window_onto_buffer(wp, buffer);
} while ((wp = next_window(wp)) != window);
readonly = Pb(P_readonly) || !can_write(buffer->b_filename);
nlines = get_file(window, buffer->b_filename, &head, &tail,
(readonly ? " [Read only]" : ""),
" [New file]");
update_sline(window); /* ensure colour is updated */
if (nlines == gf_NEWFILE) { /* no such file */
return(FALSE);
} else if (nlines >= 0) {
unsigned savecho;
/*
* Success.
*/
if (readonly) {
buffer->b_flags |= FL_READONLY;
} else {
buffer->b_flags &= ~FL_READONLY;
}
if (nlines == 0) { /* empty file */
return(TRUE);
}
/*
* We have successfully read the file in,
* so now we must link it into the buffer.
*/
replbuffer(window, head);
move_cursor(window, gotoline(buffer, line), 0);
begin_line(window, TRUE);
setpcmark(window);
/*
* We only call update_window() here because we want
* window->w_botline to be updated; we don't let it do any
* actual screen updating, for the reason explained above.
*/
savecho = echo;
echo &= ~(e_CHARUPDATE | e_SCROLL | e_REPORT | e_SHOWINFO);
update_window(window);
echo = savecho;
return(TRUE);
} else {
/*
* We failed to read in the file. An appropriate
* message will already have been printed by
* get_file() (or alloc()).
*/
if (buffer->b_filename != NULL)
free(buffer->b_filename);
if (buffer->b_tempfname != NULL)
free(buffer->b_tempfname);
buffer->b_filename = buffer->b_tempfname = NULL;
return(FALSE);
}
}
void
do_args(window)
Xviwin *window;
{
register char *tmpbuf;
int count;
register int curpos = 0;
if (numfiles == 0) {
show_message(window, "No files");
return;
}
tmpbuf = alloc((unsigned) window->w_ncols + 1);
if (tmpbuf == NULL) {
return;
}
for (count = 0; count < numfiles; count++) {
register char *sp;
if (count == curfile && curpos < window->w_ncols)
tmpbuf[curpos++] = '[';
for (sp = files[count]; curpos < window->w_ncols &&
(tmpbuf[curpos] = *sp++) != '\0'; curpos++) {
;
}
if (count == curfile && curpos < window->w_ncols)
tmpbuf[curpos++] = ']';
if (curpos < window->w_ncols)
tmpbuf[curpos++] = ' ';
}
tmpbuf[curpos < window->w_ncols ? curpos : window->w_ncols] = '\0';
show_message(window, "%s", tmpbuf);
free(tmpbuf);
}
/*
* Change the current file list to the one specified, or edit the next
* file in the current file list, or edit the next file in the list if
* no argument is given.
*/
void
do_next(window, argc, argv, force)
Xviwin *window;
int argc;
char *argv[];
bool_t force;
{
unsigned savecho;
savecho = echo;
if (argc > 0) {
int count;
/*
* Arguments given - this means a new set of filenames.
*/
if (!force && is_modified(window->w_buffer)) {
show_error(window, nowrtmsg);
return;
}
/*
* There were no files before, so start from square one.
*/
if (numfiles == 0) {
files = (char **) alloc((unsigned) argc * sizeof(char *));
if (files == NULL) {
return;
}
} else {
/*
* We can change the existing list of files.
* Free up all the individual filenames
* which we got last time.
*/
for (count = 0; count < numfiles; count++) {
free(files[count]);
}
if (argc != numfiles) {
files = (char **) realloc((char *) files,
(unsigned) argc * sizeof(char *));
if (files == NULL) {
numfiles = 0;
return;
}
}
}
/*
* Now record all the new filenames.
*/
for (count = 0; count < argc; count++) {
files[count] = strsave(argv[count]);
if (files[count] == NULL) {
/*
* Aargh. Failed half-way through.
* Clean up the mess ...
*/
while (--count >= 0)
free(files[count]);
free((char *) files);
files = NULL;
numfiles = 0;
return;
}
}
numfiles = argc;
curfile = 0;
/*
* And try to edit the first few of them.
*
* In this case, we don't want report() or
* show_file_info() to be called, because otherwise
* the messages printed by get_file() won't be seen.
*/
echo &= ~(e_SCROLL | e_REPORT | e_SHOWINFO);
(void) do_edit(curwin, force, files[0]);
/*
* This is not very good because it
* doesn't split the screen evenly for
* autosplit > 2. However, it will
* just have to do for the moment.
*/
/*
* Update the current window before
* creating any new ones.
*/
move_window_to_cursor(curwin);
while ((curfile + 1) < numfiles && can_split()) {
bool_t success;
success = do_buffer(curwin, files[++curfile]);
/*
* Make sure move_window_to_cursor() is called
* for every window before calling
* update_buffer().
*/
move_window_to_cursor(curwin);
if (!success)
break;
}
update_window(curwin);
} else if ((curfile + 1) < numfiles) {
/*
* No arguments; this is the normal usage, and
* indicates we should edit the next file in the list.
* Don't grab the next file if the current one is
* modified and not written, or we will "lose"
* files from the list.
*/
if (!force && is_modified(window->w_buffer)) {
show_error(window, nowrtmsg);
return;
}
/*
* Just edit the next file.
*/
echo &= ~(e_SCROLL | e_REPORT | e_SHOWINFO);
(void) do_edit(window, force, files[++curfile]);
move_window_to_cursor(window);
update_buffer(window->w_buffer);
} else {
show_message(window, "No more files");
}
echo = savecho;
}
/*ARGSUSED*/
void
do_rewind(window, force)
Xviwin *window;
bool_t force;
{
unsigned savecho;
if (numfiles <= 1) /* nothing to rewind */
return;
curfile = 0;
savecho = echo;
echo &= ~(e_SCROLL | e_REPORT | e_SHOWINFO);
(void) do_edit(window, force, files[0]);
move_window_to_cursor(window);
update_buffer(window->w_buffer);
echo = savecho;
}
/*
* Write out the buffer, to the given filename,
* from "line1" to "line2", forcing if necessary.
*
* If no filename given, use the buffer's filename.
*/
bool_t
do_write(window, filename, l1, l2, force)
Xviwin *window;
char *filename;
Line *l1, *l2;
bool_t force;
{
if (filename == NULL) {
filename = window->w_buffer->b_filename;
}
if (filename == NULL) {
show_error(window, "No output file");
return(FALSE);
} else {
return(writeit(window, filename, l1, l2, force));
}
}
/*
* Write to the given filename then quit.
*/
void
do_wq(window, filename, force)
Xviwin *window;
char *filename;
bool_t force;
{
if (do_write(window, filename, (Line *) NULL, (Line *) NULL, force)) {
do_quit(window, force);
}
}
/*
* Read the given file into the buffer after the specified line.
* The line may not be NULL, but should be a line in the buffer
* referenced by the passed window parameter.
*/
void
do_read(window, filename, atline)
Xviwin *window;
char *filename;
Line *atline;
{
Line *head; /* start of list of lines */
Line *tail; /* last element of list of lines */
long nlines; /* number of lines read */
nlines = get_file(window, filename, &head, &tail, "", " No such file");
/*
* If nlines > 0, we need to insert the lines returned into
* the buffer. Otherwise, either the file is empty or an error
* message has already been printed: in either case, we don't
* need to do anything.
*/
if (nlines > 0) {
/*
* We want to see the message printed by
* get_file() here, not the message printed by
* report().
*/
echo &= ~e_REPORT;
repllines(window, atline->l_next, 0L, head);
echo |= e_REPORT;
update_buffer(window->w_buffer);
/*
* Move the cursor to the first character
* of the file we just read in.
*/
move_cursor(window, atline->l_next, 0);
begin_line(window, TRUE);
}
}
/*
* Edit alternate file. Called when control-^ is typed.
*/
void
do_alt_edit(window)
Xviwin *window;
{
if (altfilename == NULL) {
show_error(window, "No alternate file to edit");
} else {
if (do_buffer(window, altfilename)) {
move_window_to_cursor(curwin);
update_window(curwin);
}
}
}
void
do_compare()
{
Xviwin *w;
enum mvtype incres;
Posn pos1, pos2;
w = next_window(curwin);
if (w == curwin) {
show_error(curwin, "No other buffers to compare");
} else if (w->w_buffer == curbuf) {
show_error(curwin, "Next window has same buffer");
} else {
pos1 = *(curwin->w_cursor);
pos2 = *(w->w_cursor);
while ((incres = inc(&pos1)) == inc(&pos2)) {
if (incres == mv_EOL) {
continue;
} else if (incres == mv_NOMOVE) {
(void) dec(&pos1);
(void) dec(&pos2);
break;
} else {
if (gchar(&pos1) != gchar(&pos2)) {
break;
}
}
}
if (gchar(&pos1) == '\0' && pos1.p_index > 0) {
(void) dec(&pos1);
}
if (gchar(&pos2) == '\0' && pos2.p_index > 0) {
(void) dec(&pos2);
}
move_cursor(curwin, pos1.p_line, pos1.p_index);
move_cursor(w, pos2.p_line, pos2.p_index);
move_window_to_cursor(w);
cursupdate(w);
wind_goto(w);
}
}
static bool_t
more_files()
{
int n;
n = numfiles - (curfile + 1);
if (n > 0) {
show_error(curwin, "%d more file%s to edit", n, (n > 1) ? "s" : "");
return(TRUE);
} else {
return(FALSE);
}
}